package com.example.recordhelper.camera;

import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;
import com.example.recordhelper.camera.dao.RecordPlanRepo;
import com.example.recordhelper.camera.dao.VideoSourceRepo;
import com.example.recordhelper.camera.po.RecordPlanPO;
import com.example.recordhelper.camera.po.VideoSourcePO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.config.CronTask;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author: zhouyuehan
 * @Date: 2024/10/25 17:43
 */
@Slf4j
@Component
public class RecordService {

    final VideoSourceRepo videoSourceRepo;
    final RecordPlanRepo recordPlanRepo;
    final RecordTaskExecutor recordTaskExecutor;
    final Set<Long> registeredPlanIds = new HashSet<>();
    final DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar;

    public RecordService(VideoSourceRepo videoSourceRepo, RecordPlanRepo recordPlanRepo, RecordTaskExecutor recordTaskExecutor, DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar) {
        this.videoSourceRepo = videoSourceRepo;
        this.recordPlanRepo = recordPlanRepo;
        this.recordTaskExecutor = recordTaskExecutor;
        this.dynamicScheduledTaskRegistrar = dynamicScheduledTaskRegistrar;
    }

    @Scheduled(fixedRate = 10000)
    public void refresh() {
        List<VideoSourcePO> videoSourcePOS = videoSourceRepo.findAll();
        for (VideoSourcePO v : videoSourcePOS) {
            String url = v.getSourceUrl();
            if (url == null || url.isEmpty()) {
                log.error("[id={}] 视频源URL为空", v.getSourceId());
                continue;
            }
            List<RecordPlanPO> recordPlanPOS = recordPlanRepo.findBySourceId(v.getSourceId());
            for (RecordPlanPO p : recordPlanPOS) {
                if (!registeredPlanIds.contains(p.getPlanId())) {
                    registerPlan(p, v);
                }
            }
        }

        List<RecordPlanPO> recordPlanPOS = recordPlanRepo.findAll();
        Set<Long> currentPlanIds = recordPlanPOS.stream().map(RecordPlanPO::getPlanId).collect(Collectors.toSet());
        for (Long id : registeredPlanIds) {
            if (!currentPlanIds.contains(id)) {
                deregisterPlan(id);
            }
        }
    }

    private void registerPlan(RecordPlanPO plan, VideoSourcePO videoSource) {
        ZonedDateTime now = ZonedDateTime.now();
        Optional<ZonedDateTime> lastBeginTime = ExecutionTime.forCron(new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING)).parse(plan.getBeginCron())).lastExecution(now);
        Optional<ZonedDateTime> lastEndTime = ExecutionTime.forCron(new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING)).parse(plan.getEndCron())).lastExecution(now);
        Optional<ZonedDateTime> nextBeginTime = ExecutionTime.forCron(new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING)).parse(plan.getBeginCron())).nextExecution(now);
        Optional<ZonedDateTime> nextEndTime = ExecutionTime.forCron(new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING)).parse(plan.getEndCron())).nextExecution(now);
        if(lastBeginTime.isEmpty()||nextEndTime.isEmpty()||lastEndTime.isEmpty()|| nextBeginTime.isEmpty()){
            throw new IllegalArgumentException("parse cron error");
        }
        dynamicScheduledTaskRegistrar.addCronTask("begin_" + plan.getPlanId(), new CronTask(() -> {
            recordTaskExecutor.newTask(plan.getPlanId(), videoSource.getSourceUrl(), videoSource.getSourceId(), videoSource.getForwardUrl());
        }, plan.getBeginCron()));
        dynamicScheduledTaskRegistrar.addCronTask("end_" + plan.getPlanId(), new CronTask(() -> {
            recordTaskExecutor.stopTask(plan.getPlanId());
        }, plan.getEndCron()));
        if(lastEndTime.get().isBefore(lastBeginTime.get())&&lastBeginTime.get().isBefore(now)&&now.isBefore(nextEndTime.get())&&nextEndTime.get().isBefore(nextBeginTime.get())){
            //当前任务应该正在执行
            recordTaskExecutor.newTask(plan.getPlanId(), videoSource.getSourceUrl(), videoSource.getSourceId(), videoSource.getForwardUrl());
        }
        registeredPlanIds.add(plan.getPlanId());
    }

    private void deregisterPlan(Long planId) {
        dynamicScheduledTaskRegistrar.cancelCronTask("begin_" + planId);
        dynamicScheduledTaskRegistrar.cancelCronTask("end_" + planId);
        recordTaskExecutor.stopTask(planId);
        registeredPlanIds.remove(planId);
    }
}
